package com.prizedraw.prize_draw.controller;


import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.prizedraw.prize_draw.common.dto.DrawInfoDto;
import com.prizedraw.prize_draw.common.dto.LuckyDrawDto;
import com.prizedraw.prize_draw.common.lang.Result;
import com.prizedraw.prize_draw.common.sheet.LuckUser;
import com.prizedraw.prize_draw.entity.*;
import com.prizedraw.prize_draw.mapper.DrawRecordMapper;
import com.prizedraw.prize_draw.service.DepartmentService;
import com.prizedraw.prize_draw.service.DrawService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 *  抽奖控制层
 * </p>
 *
 * @author czj666
 * @since 2021-09-07
 */

@Slf4j
@RestController
@RequestMapping("/draw")
public class DrawController extends BaseController {

    @Autowired
    DepartmentService departmentService;

    @Autowired
    DrawRecordMapper drawRecordMapper;

    /**
     * 显示所有活动
     * @param drawName
     * @return
     */
    @GetMapping("/list")
    @PreAuthorize("hasAuthority('draw:list')")
    public Result list(String drawName){
        Page<Draw> pageData = drawService.page(getPage(),(new QueryWrapper<Draw>()
                        .like(StrUtil.isNotBlank(drawName),"title",drawName)));
        for (Draw draw : pageData.getRecords()) {
            List<Department> ds = departmentService.list(new QueryWrapper<Department>()
                    .inSql("id", "select dept_id from department_draw where draw_id = " + draw.getId()));
            draw.setDrawScope(ds);

            List<Prize> prize = prizeService.list(new QueryWrapper<Prize>().eq("draw_id", draw.getId()));
            draw.setPrizeList(prize);
        }

        return Result.succ(pageData);
    }

    /**
     * 发布活动
     * @param draw
     * @return
     */
    @Transactional
    @PostMapping("/save")
    @PreAuthorize("hasAuthority('draw:save')")
    public Result save(@Validated @RequestBody Draw draw){
        System.out.println(draw);
        //判断活动参与人数是否大于奖项人数
        if (drawService.matchNum(draw)){
            //保存活动信息
            drawService.saveInfo(draw);
            return Result.succ("");
        }else {
            return Result.fail("活动参与人数应该大于等于奖项人数！");
        }


    }

    /**
     * 删除活动
     * @param id
     * @return
     */
    @Transactional
    @PostMapping("/delete/{id}")
    @PreAuthorize("hasAuthority('draw:delete')")
    public Result delete(@PathVariable("id") Integer id){
        Draw draw = drawService.getById(id);
        //判断是否活动已经结束，如果已经结束则无法删除
        if (draw.getState() == 0){
            return Result.fail("无法删除结束的活动！");
        }else {
            //删除活动表
            drawService.removeById(id);
            //删除部门-活动表
            departmentDrawService.remove(new QueryWrapper<DepartmentDraw>().eq("draw_id",id));
            //删除奖项表
            prizeService.remove(new QueryWrapper<Prize>().eq("draw_id",id));
        }
        return Result.succ("");
    }

    /**
     * 根据活动ID获得活动信息
     * @param id
     * @return
     */
    @GetMapping("/info/{id}")
    @PreAuthorize("hasAuthority('draw:list')")
    public Result info(@PathVariable("id") Integer id){

        Draw draw = drawService.getById(id);
        //判断是否活动已经结束，如果已经结束则无法编辑
        if (draw.getState() == 0) {
            return Result.fail("无法编辑结束的活动！");
        }else {
            Draw drawInfo = drawService.listDrawByDrawId(id);
            return Result.succ(drawInfo);
        }

    }

    /**
     * 更新活动
     * @param draw
     * @return
     */
    @PostMapping("/update")
    @PreAuthorize("hasAuthority('draw:update')")
    public Result update(@Validated @RequestBody Draw draw){
        //判断是否活动已经结束，如果已经结束则无法更新
        if (draw.getState() == 0){
            return Result.fail("无法更新结束的活动！");
        }else {
            drawService.updateById(draw);
        }

        return Result.succ("");
    }

    /**
     * 通过活动ID显示参与的人
     * @param id
     * @return
     */
    @GetMapping("/listUserByDrawId/{id}")
    public Result listUserByDrawId(@PathVariable("id") Integer id){

        List<SysUser> users = drawService.listUserByDrawId(id);

        return Result.succ(users);
    }

    /**
     * 根据活动ID获取参与人和抽奖信息
     * @param id
     * @return
     */
    @GetMapping ("/listDrawInfoByDrawId/{id}")
    public Result<DrawInfoDto> listDrawInfoByDrawId(@PathVariable("id") Integer id){

        List<SysUser> users = drawService.listUserByDrawId(id);
        Draw draw = drawService.listDrawByDrawId(id);

        DrawInfoDto drawInfoDto = new DrawInfoDto();
        drawInfoDto.setId(draw.getId());
        drawInfoDto.setTitle(draw.getTitle());
        drawInfoDto.setRemark(draw.getRemark());
        drawInfoDto.setReleaseTime(draw.getReleaseTime());
        drawInfoDto.setState(draw.getState());
        drawInfoDto.setDrawScope(draw.getDrawScope());
        drawInfoDto.setPrizeList(draw.getPrizeList());
        drawInfoDto.setUserList(users);
        return Result.succ(drawInfoDto);

    }

    /**
     * 获取抽奖ID进行抽奖
     * @param id
     * @return
     */
    @GetMapping("/start/{id}")
    @Transactional
    @PreAuthorize("hasAuthority('draw:save')")
    public Result start(@PathVariable("id") Integer id){
        //如果活动状态已结束不允许抽奖
        Draw draw = drawService.getById(id);
        if (draw.getState() != 1){
            return Result.fail("活动已经结束！");
        }else {
            //获取全部userId放在集合
            List<SysUser> users = drawService.listUserByDrawId(id);
            List<String> userIds = users.stream().map(u -> u.getUserId()).collect(Collectors.toList());

            //过滤重复的userId
            Set<String> sUserIds = new HashSet<>(userIds);
            List<String> lUserIds = new ArrayList<>(sUserIds);

            //随机集合
            Collections.shuffle(lUserIds);

            //转化成链式集合
            LinkedList<String> linkedUserIds = new LinkedList<>(lUserIds);

            //查找奖项分配
            List<Prize> prizes = prizeService.list(new QueryWrapper<Prize>().eq("draw_id", id));

            //最终抽奖结果集合 -- 用于抽奖完成的结果进行返回，里面包含每个奖项的中奖用户
            List<LuckyDrawDto> luckyDrawDtos = new ArrayList<>();
            //用于临时保存每个奖项的中奖用户userID集合
            List<Integer> luckyDrawUserIds = new ArrayList<>();
            //全部存入数据库的中奖记录集合
            List<DrawRecord> drawRecords = new ArrayList<>();

            //遍历活动中每个奖项
            for (Prize prize : prizes) {
                LuckyDrawDto luckyDrawDto = new LuckyDrawDto();
                luckyDrawUserIds.clear();
                Integer drawNum = prize.getDrawNum();

                //抽出中奖userId集合
                for (int i = 0;i < drawNum;i ++){
                    luckyDrawUserIds.add(Integer.parseInt(linkedUserIds.poll()));
                }

                List<SysUser> luckyUsers = new ArrayList<>();

                //根据userId集合查找每个中奖名单的人的信息
                for (Integer luckyDrawUserId : luckyDrawUserIds) {

                    SysUser user = sysUserService.getOne(new QueryWrapper<SysUser>()
                            .eq("user_id", luckyDrawUserId)
                            .select("id","user_id","user_name","gender","phone_num","hiredate"));
//                    List<SysRole> sysRoles = sysUserService.listRoleByUserId(user.getUserId());
//                    user.setSysRoles(sysRoles);
                    luckyUsers.add(user);

                    //整理中奖记录放在一个集合里
                    DrawRecord drawRecord = new DrawRecord();
                    drawRecord.setUserId(user.getId());
                    drawRecord.setDrawId(id);
                    drawRecord.setPrizeId(prize.getId());

                    drawRecords.add(drawRecord);
                }

                //整理出抽奖结果
                luckyDrawDto.setDrawName(draw.getTitle());
                luckyDrawDto.setTitle(prize.getTitle());
                luckyDrawDto.setPrizeName(prize.getPrizeName());
                luckyDrawDto.setPrizeId(prize.getId());
                luckyDrawDto.setDrawId(prize.getDrawId());
                luckyDrawDto.setUsers(luckyUsers);
                luckyDrawDtos.add(luckyDrawDto);

            }
            //整理中奖记录存储到数据库
            drawRecordService.saveBatch(drawRecords);

            //活动状态设置成0
            drawService.update(new UpdateWrapper<Draw>().set("state",0).eq("id",id));

            return Result.succ(luckyDrawDtos);
        }

    }

    /**
     * 根据活动ID获取奖项信息+中奖用户
     * @param id
     * @return
     */
    @GetMapping("/getLuckyUsersById/{id}")
    public Result getLuckyUsersByDrawId(@PathVariable("id") Integer id){

        List<LuckyDrawDto> luckyDrawDtos = drawService.getLuckyUsersById(id);

        return Result.succ(luckyDrawDtos);
    }

    //根据用户ID查所属部门的活动
    @GetMapping("/getDeptDrawById/{id}")
    public Result getDeptDrawById(@PathVariable("id") Integer id){
        List<Draw> drawList = drawService.getDeptDrawById(id);
        return Result.succ(drawList);
    }
    //根据用户ID查历史参与的活动
    @GetMapping("/getUserDrawById/{id}")
    public Result getUserDrawById(@PathVariable("id") Integer id){
        List<Draw> drawList = drawService.getUserDrawById(id);
        return Result.succ(drawList);
    }

    //把结果页面传入中奖活动ID 并封装到二维码里
    @GetMapping("/generateDrawInfoById/{id}")
    public Result generateDrawInfoById(@PathVariable("id") Integer id) throws IOException {
        QrConfig config = new QrConfig(150, 150);
        BufferedImage bufferedImage = QrCodeUtil.generate("http://localhost:8080/drawInfo/"+id,config);
        //以JPEG格式向客户端发送
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "jpg",byteArrayOutputStream);

        BASE64Encoder base64Encoder = new BASE64Encoder();
        String str = "data:image/jpeg;base64,";
        String base64Code = str + base64Encoder.encode(byteArrayOutputStream.toByteArray());

        return Result.succ(
                MapUtil.builder()
                .put("base64Code",base64Code)
                .build()
        );
    }


    /**
     * 根据活动ID下载抽奖名单execl表格
     * @param id
     * @param response
     * @return
     */
    @GetMapping("/exportLuckUser/{id}")
    @PreAuthorize("hasAuthority('draw:save')")
    public void exportLuckInfo(@PathVariable("id") Integer id, HttpServletResponse response) throws IOException {
        // 告诉前端响应类型的内容
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

        Draw draw = drawService.getById(id);
        // 用来设置下载文件名称，并且前端能使用到，前端使用全局方法decodeURI(string)
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition:","attachement;filename="+ URLUtil.encode(StrUtil.format("{}-中奖结果", draw.getTitle()),"utf-8")+".xlsx");
        response.setCharacterEncoding("utf-8");

        // 查询该活动id的中奖名单信息
        List<LuckUser> luckUsers = drawRecordMapper.selectByDrawId(id);

        OutputStream outputStream = response.getOutputStream();
        ExcelWriter excelWriter = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true);
        Sheet sheet = new Sheet(1, 0, LuckUser.class);
        sheet.setSheetName(StrUtil.format("{}-中奖结果", draw.getTitle()));

        excelWriter.write(luckUsers, sheet);
        outputStream.flush();
        excelWriter.finish();
        outputStream.close();
    }
}
